package com.myk.game.shapechesstool.views;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.graphics.Xfermode;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;

import com.blankj.utilcode.util.AppUtils;
import com.blankj.utilcode.util.CollectionUtils;
import com.blankj.utilcode.util.GsonUtils;
import com.blankj.utilcode.util.LogUtils;
import com.myk.game.shapechesstool.models.ArrowTipInfo;
import com.myk.game.shapechesstool.models.ChessInfo;
import com.myk.game.shapechesstool.models.FallTipInfo;
import com.myk.game.shapechesstool.models.HistoryInfo;
import com.myk.game.shapechesstool.IGameUI;
import com.myk.game.shapechesstool.R;
import com.myk.game.shapechesstool.data.ActionState;
import com.myk.game.shapechesstool.data.SoundData;
import com.myk.game.shapechesstool.models.SituationInfo;

import java.lang.annotation.ElementType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;

public class ChessBoardView extends View {

    private Paint mPaint; //动态元素画笔
    private Bitmap bgBuffer; //棋盘背景二级缓冲（界面刷新时不用重新计算和绘制）
    private List<ChessInfo> pieces; //棋盘上的棋子信息集合
    private List<ChessInfo> symmetryPieces; //形成了对称的棋子信息集合
    private FallTipInfo fallTip; //最新落子提示信息
    private ArrowTipInfo arrowTip; //移动箭头提示信息
    private Point tempPoint; //切换到移子挤子状态时记录的临时坐标
    private Bitmap blackPiece, whitePiece; //棋子图片
    private Bitmap symmetryTip; //成型棋子提示图片
    private Xfermode fallTipXfermode;//绘制落子提示的混合模式
    private int col, row; //棋盘的坐标大小
    private float firstX, firstY; //棋盘的绘制原点，隐式的保存了绘制的偏移信息
    private float placeSize; //棋盘交叉点的间隔距离
    private IGameUI iGameUI; //对游戏外部UI的引用
    private int maxScore; //获胜需要的最大分数
    private int blackScore, whiteScore; //当前得分
    private ActionState actionState; //当前行动状态
    private List<String> historyList; //历史记录列表
    private List<String> turnHistoryList; //反历史记录列表
    private float dX, dY, uX, uY;   //触摸事件按下抬起时的点
    private long dT, uT; //触摸事件按下抬起的时间

    public ChessBoardView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (bgBuffer != null && pieces != null) {
            canvas.drawBitmap(bgBuffer, 0, 0, mPaint);
            for (ChessInfo piece : pieces) {
                canvas.drawBitmap(piece.isBlack ? blackPiece : whitePiece, piece.startX, piece.startY, mPaint);
                /*mPaint.setColor(piece.isBlack ? Color.WHITE : Color.BLACK); //辅助算法用的
                canvas.drawText(piece.col + "," + piece.row, piece.startX + placeSize / 2, piece.startY + placeSize / 2, mPaint);
                mPaint.setColor(Color.RED);
                canvas.drawText(piece.getOrderNo() + "", piece.startX + placeSize / 2, piece.startY + placeSize, mPaint);*/
            }
            for (ChessInfo symmetryPiece : symmetryPieces) {
                canvas.drawBitmap(symmetryTip, symmetryPiece.startX, symmetryPiece.startY, mPaint);
            }
            if (fallTip != null) {
                drawFocus(canvas);
            }
            if (arrowTip != null) {
                drawArrow(canvas);
            }
        }
    }

    //绘制聚焦
    private void drawFocus(Canvas canvas) {
        int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), mPaint);
        mPaint.setColor(0x99CC0000);
        canvas.drawRect(fallTip.startX, fallTip.startY, fallTip.endX, fallTip.endY, mPaint);
        mPaint.setXfermode(fallTipXfermode);
        canvas.drawCircle(fallTip.enterX, fallTip.enterY, fallTip.radius, mPaint);
        mPaint.setColor(0xFF000000);
        mPaint.setXfermode(null);
        canvas.restoreToCount(saveCount);
    }

    //绘制箭头
    private void drawArrow(Canvas canvas) {
        mPaint.setColor(0x99CC0000);
        mPaint.setStrokeWidth(placeSize / 8f);
        mPaint.setStrokeCap(Paint.Cap.BUTT);
        canvas.drawLine(arrowTip.startX, arrowTip.startY, arrowTip.endX, arrowTip.endY, mPaint);
        drawTriangle(canvas, arrowTip.startX, arrowTip.startY, arrowTip.endX, arrowTip.endY, placeSize / 2f, placeSize / 3f);
        mPaint.setColor(0xFF000000);
    }

    //绘制线条箭头
    private void drawTriangle(Canvas canvas, float fromX, float fromY, float toX, float toY, float height, float bottom) {
        float juli = (float) Math.sqrt((toX - fromX) * (toX - fromX) + (toY - fromY) * (toY - fromY));// 获取线段距离
        float juliX = toX - fromX;// 有正负，不要取绝对值
        float juliY = toY - fromY;// 有正负，不要取绝对值
        float dianX = toX - (height / juli * juliX);
        float dianY = toY - (height / juli * juliY);
        Path path = new Path(); //终点的箭头
        path.moveTo(toX, toY);// 此点为三边形的起点
        path.lineTo(dianX + (bottom / juli * juliY), dianY - (bottom / juli * juliX));
        path.lineTo(dianX - (bottom / juli * juliY), dianY + (bottom / juli * juliX));
        path.close(); // 使这些点构成封闭的三边形
        canvas.drawPath(path, mPaint);
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            dX = event.getX();
            dY = event.getY();
            dT = System.currentTimeMillis();
        }
        else if (event.getAction() == MotionEvent.ACTION_UP) {
            uX = event.getX();
            uY = event.getY();
            uT = System.currentTimeMillis();
            handleEvent();
        }
        return true;
    }

    /**
     * 加分操作
     */
    public void addScore(boolean isBlack) {
        if (isBlack) {
            if (blackScore == maxScore) {
                blackScore = 0;
                iGameUI.printlnMsg("黑方得分置0");
            }
            else {
                blackScore++;
                iGameUI.printlnMsg("黑方得分加1");
            }
        }
        else {
            if (whiteScore == maxScore) {
                whiteScore = 0;
                iGameUI.printlnMsg("白方得分置0");
            }
            else {
                whiteScore++;
                iGameUI.printlnMsg("白方得分加1");
            }
        }
        refreshUI();
    }

    /**
     * 切换行动方
     */
    public void changeSide(boolean changeToBlack) {
        if (changeToBlack) {
            if (checkState(ActionState.WAIT_FALL_WHITE)) {
                changeState(ActionState.WAIT_FALL_BLACK);
                iGameUI.printlnMsg("主动切换到黑方行动");
            }
            else if (checkState(ActionState.THEN_MOVE_WHITE)) {
                iGameUI.toast("不能切换，请先让白棋完成移动");
            }
            else if (checkState(ActionState.PUSH_AWAY_BLACK)) {
                iGameUI.toast("不能切换，请先落下黑棋的位置");
            }
            else if (checkState(ActionState.REMOVE_SHAPED_WHITE)) {
                iGameUI.toast("不能切换，请先让白棋提走成型棋子");
            }
            else if (checkState(ActionState.ENFORCE_WHITE)) {
                changeState(ActionState.ENFORCE_BLACK);
                iGameUI.printlnMsg("切换到黑方持续落子");
            }
            else if (checkState(ActionState.CONTINUE_DELETE)) {
                changeState(ActionState.WAIT_FALL_BLACK);
                iGameUI.printlnMsg("主动切换到黑方行动");
            }
        }
        else {
            if (checkState(ActionState.WAIT_FALL_BLACK)) {
                changeState(ActionState.WAIT_FALL_WHITE);
                iGameUI.printlnMsg("主动切换到白方行动");
            }
            else if (checkState(ActionState.THEN_MOVE_BLACK)) {
                iGameUI.toast("不能切换，请先让黑棋完成移动");
            }
            else if (checkState(ActionState.PUSH_AWAY_WHITE)) {
                iGameUI.toast("不能切换，请先落下白棋的位置");
            }
            else if (checkState(ActionState.REMOVE_SHAPED_BLACK)) {
                iGameUI.toast("不能切换，请先让黑棋提走成型棋子");
            }
            else if (checkState(ActionState.ENFORCE_BLACK)) {
                changeState(ActionState.ENFORCE_WHITE);
                iGameUI.printlnMsg("切换到白方持续落子");
            }
            else if (checkState(ActionState.CONTINUE_DELETE)) {
                changeState(ActionState.WAIT_FALL_WHITE);
                iGameUI.printlnMsg("主动切换到白方行动");
            }
        }
    }

    /**
     * 切换到自动转换模式
     */
    public void autoExchange() {
        if (checkState(ActionState.WAIT_FALL_BLACK)) {
            changeState(ActionState.WAIT_FALL_WHITE);
            iGameUI.printlnMsg("切换到白方落子");
        }
        else if (checkState(ActionState.WAIT_FALL_WHITE)) {
            changeState(ActionState.WAIT_FALL_BLACK);
            iGameUI.printlnMsg("切换到黑方落子");
        }
        else if (checkState(ActionState.ENFORCE_BLACK)) {
            changeState(ActionState.WAIT_FALL_BLACK);
            iGameUI.printlnMsg("切换到黑方落子");
        }
        else if (checkState(ActionState.ENFORCE_WHITE)) {
            changeState(ActionState.WAIT_FALL_WHITE);
            iGameUI.printlnMsg("切换到白方落子");
        }
        else if (checkState(ActionState.CONTINUE_DELETE)) {
            if (guessActionSide()) {
                changeState(ActionState.WAIT_FALL_BLACK);
                iGameUI.printlnMsg("切换到黑方落子");
            }
            else {
                changeState(ActionState.WAIT_FALL_WHITE);
                iGameUI.printlnMsg("切换到白方落子");
            }
        }
    }

    /**
     * 切换到强制持续模式
     */
    public void forceContinue() {
        if (checkState(ActionState.WAIT_FALL_BLACK)) {
            changeState(ActionState.ENFORCE_BLACK);
            iGameUI.printlnMsg("切换到黑方持续落子");
        }
        else if (checkState(ActionState.WAIT_FALL_WHITE)) {
            changeState(ActionState.ENFORCE_WHITE);
            iGameUI.printlnMsg("切换到白方持续落子");
        }
        else if (checkState(ActionState.THEN_MOVE_BLACK)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        else if (checkState(ActionState.THEN_MOVE_WHITE)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        else if (checkState(ActionState.PUSH_AWAY_WHITE)) {
            iGameUI.toast("不能切换，请先落下白棋的位置");
        }
        else if (checkState(ActionState.PUSH_AWAY_BLACK)) {
            iGameUI.toast("不能切换，请先落下黑棋的位置");
        }
        else if (checkState(ActionState.REMOVE_SHAPED_BLACK)) {
            changeState(ActionState.ENFORCE_BLACK);
            iGameUI.printlnMsg("切换到黑方持续落子");
        }
        else if (checkState(ActionState.REMOVE_SHAPED_WHITE)) {
            changeState(ActionState.ENFORCE_WHITE);
            iGameUI.printlnMsg("切换到白方持续落子");
        }
        else if (checkState(ActionState.ENFORCE_BLACK)) {
            changeState(ActionState.ENFORCE_WHITE);
            iGameUI.printlnMsg("切换到白方持续落子");
        }
        else if (checkState(ActionState.ENFORCE_WHITE)) {
            changeState(ActionState.ENFORCE_BLACK);
            iGameUI.printlnMsg("切换到黑方持续落子");
        }
        else if (checkState(ActionState.CONTINUE_DELETE)) {
            changeState(ActionState.ENFORCE_BLACK);
            iGameUI.printlnMsg("切换到黑方持续落子");
        }
    }

    /**
     * 切换到删除模式
     */
    public void deleteMode() {
        if (checkState(ActionState.WAIT_FALL_BLACK)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        if (checkState(ActionState.WAIT_FALL_WHITE)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        if (checkState(ActionState.THEN_MOVE_BLACK)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        if (checkState(ActionState.THEN_MOVE_WHITE)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        if (checkState(ActionState.PUSH_AWAY_WHITE)) {
            iGameUI.toast("不能切换，请先落下白棋的位置");
        }
        if (checkState(ActionState.PUSH_AWAY_BLACK)) {
            iGameUI.toast("不能切换，请先落下黑棋的位置");
        }
        if (checkState(ActionState.REMOVE_SHAPED_BLACK)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        if (checkState(ActionState.REMOVE_SHAPED_WHITE)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        if (checkState(ActionState.ENFORCE_BLACK)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        if (checkState(ActionState.ENFORCE_WHITE)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        if (checkState(ActionState.BLACK_WIN)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        if (checkState(ActionState.WHITE_WIN)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
    }

    /**
     * 初始化棋盘
     */
    public void initChessBoard(IGameUI iGameUI, int col, int row, int maxScore) {
        post(() -> {
            this.iGameUI = iGameUI;
            this.col = col;
            this.row = row;
            this.maxScore = maxScore;
            calculateParameter();
            initPaint();
            drawBgBuffer();
            this.pieces = new ArrayList<>();
            this.fallTip = null;
            this.arrowTip = null;
            this.symmetryPieces = new ArrayList<>();
            this.blackPiece = getBitmap(R.mipmap.ic_black, placeSize, placeSize);
            this.whitePiece = getBitmap(R.mipmap.ic_white, placeSize, placeSize);
            this.symmetryTip = getBitmap(R.mipmap.ic_star4, placeSize, placeSize);
            this.fallTipXfermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
            this.maxScore = maxScore;
            this.blackScore = 0;
            this.whiteScore = 0;
            this.historyList = new ArrayList<>();
            this.turnHistoryList = new ArrayList<>();
            changeState(ActionState.WAIT_FALL_BLACK); //默认黑棋先行
            addHistory(); //开局保存到历史记录里
            iGameUI.clearMsg();
            iGameUI.printlnMsg("对局开始：黑方先行，先得" + maxScore + "分获胜");
            invalidate();
        });
    }

    /**
     * 从外部来源还原历史记录
     */
    public void restoreExternalHistory(IGameUI iGameUI, HistoryInfo historyInfo) {
        this.iGameUI = iGameUI;
        this.col = historyInfo.getCol();
        this.row = historyInfo.getRow();
        this.maxScore = historyInfo.getMaxScore();
        this.historyList = new ArrayList<>();
        this.turnHistoryList = new ArrayList<>();
        this.symmetryPieces = historyInfo.getSymmetryPieces();
        this.tempPoint = historyInfo.getTempPoint();
        this.whiteScore = historyInfo.getWhiteScore();
        this.blackScore = historyInfo.getBlackScore();
        this.actionState = historyInfo.getActionState();
        calculateParameter();
        drawBgBuffer();
        this.blackPiece = getBitmap(R.mipmap.ic_black, placeSize, placeSize);
        this.whitePiece = getBitmap(R.mipmap.ic_white, placeSize, placeSize);
        this.symmetryTip = getBitmap(R.mipmap.ic_star4, placeSize, placeSize);
        this.pieces = new ArrayList<>();
        List<ChessInfo> chessInfoList = historyInfo.getPieces();
        for (ChessInfo chessInfo : chessInfoList) {
            fallPiece(chessInfo.isBlack, chessInfo.col, chessInfo.row);
        }
        FallTipInfo fallTipInfo = historyInfo.getFallTip();
        if (fallTipInfo != null) {
            setFallTip(fallTipInfo.pCol, fallTipInfo.pRow);
        }
        ArrowTipInfo arrowTip = historyInfo.getArrowTip();
        if (tempPoint != null && arrowTip != null) {
            setArrowTip(arrowTip.pCol, arrowTip.pRow);
        }
        addHistory();
        refreshUI();
        invalidate();
    }

    /**
     * 返回历史记录
     */
    public List<String> getHistoryList() {
        return historyList;
    }

    /**
     * 返回上一步
     */
    public void undoOnce() {
        if (historyList.size() <= 1) {
            iGameUI.toast("已在最初回合");
        }
        else {
            String curHistory = historyList.get(historyList.size() - 1);
            String lastHistory = historyList.get(historyList.size() - 2);
            historyList.remove(curHistory);
            turnHistoryList.add(curHistory);
            HistoryInfo lastHistoryInfo = GsonUtils.fromJson(lastHistory, HistoryInfo.class);
            restoreInternalHistory(lastHistoryInfo);
        }
    }

    /**
     * 还原下一步
     */
    public void redoOnce() {
        if (turnHistoryList.size() > 0) {
            String laterHistory = turnHistoryList.get(turnHistoryList.size() - 1);
            turnHistoryList.remove(laterHistory);
            historyList.add(laterHistory);
            HistoryInfo laterHistoryInfo = GsonUtils.fromJson(laterHistory, HistoryInfo.class);
            restoreInternalHistory(laterHistoryInfo);
        }
        else {
            iGameUI.toast("已在最新回合");
        }
    }

    /**
     * 返回当前局面记录
     */
    public SituationInfo getSituation() {
        SituationInfo situationInfo = new SituationInfo();
        situationInfo.setMaxScore(maxScore);
        situationInfo.setBlackScore(blackScore);
        situationInfo.setWhiteScore(whiteScore);
        situationInfo.setActionState(actionState.tip);
        int[][] girds = new int[row][col];
        for (ChessInfo piece : pieces) {
            girds[piece.row][piece.col] = piece.isBlack ? 1 : 2;
        }
        for (ChessInfo piece : symmetryPieces) {
            girds[piece.row][piece.col] = piece.isBlack ? 3 : 4;
        }
        situationInfo.setGirds(girds);
        if (fallTip != null) {
            situationInfo.setFallPoint(new Point(fallTip.pCol, fallTip.pRow));
        }
        if (arrowTip != null) {
            situationInfo.setArrowPoint(new Point(arrowTip.pCol, arrowTip.pRow));
        }
        return situationInfo;
    }

    /**
     * 根据局面还原棋盘
     */
    public void restoreSituation(IGameUI iGameUI, SituationInfo situationInfo) {
        HistoryInfo historyInfo = new HistoryInfo();
        historyInfo.setMaxScore(situationInfo.getMaxScore());
        historyInfo.setBlackScore(situationInfo.getBlackScore());
        historyInfo.setWhiteScore(situationInfo.getWhiteScore());
        ActionState actionState = ActionState.getActionStateByTip(situationInfo.getActionState());
        historyInfo.setActionState(actionState);
        int[][] girds = situationInfo.getGirds();
        List<ChessInfo> pieces = new ArrayList<>();
        List<ChessInfo> symmetryPieces = new ArrayList<>();
        for (int row = 0; row < girds.length; row++) {
            for (int col = 0; col < girds[row].length; col++) {
                if (girds[row][col] == 1) {
                    pieces.add(new ChessInfo(true, col, row, -1, -1));
                }
                else if (girds[row][col] == 2) {
                    pieces.add(new ChessInfo(false, col, row, -1, -1));
                }
                else if (girds[row][col] == 3) {
                    ChessInfo chessInfo = new ChessInfo(true, col, row, -1, -1);
                    pieces.add(chessInfo);
                    symmetryPieces.add(chessInfo);
                }
                else if (girds[row][col] == 4) {
                    ChessInfo chessInfo = new ChessInfo(false, col, row, -1, -1);
                    pieces.add(chessInfo);
                    symmetryPieces.add(chessInfo);
                }
            }
        }
        historyInfo.setCol(girds[0].length);
        historyInfo.setRow(girds.length);
        historyInfo.setPieces(pieces);
        historyInfo.setSymmetryPieces(symmetryPieces);
        Point fallPoint = situationInfo.getFallPoint();
        Point arrowPoint = situationInfo.getArrowPoint();
        if (fallPoint != null) {
            historyInfo.setFallTip(new FallTipInfo(fallPoint.x, fallPoint.y, -1, -1, -1, -1, -1, -1, -1));
        }
        if (arrowPoint != null && fallPoint != null) {
            historyInfo.setTempPoint(new Point(fallPoint.x, fallPoint.y));
            historyInfo.setArrowTip(new ArrowTipInfo(arrowPoint.x, arrowPoint.y, -1, -1, -1, -1));
        }
        restoreExternalHistory(iGameUI, historyInfo);
    }

    /**
     * 设置历史纪录
     */
    public void setHistoryList(List<String> historyList) {
        this.historyList = historyList;
    }

    //把当前状态和数据添加到历史记录中
    private void addHistory() {
        HistoryInfo historyInfo = new HistoryInfo();
        historyInfo.setPieces(pieces);
        historyInfo.setSymmetryPieces(symmetryPieces);
        historyInfo.setFallTip(fallTip);
        historyInfo.setArrowTip(arrowTip);
        historyInfo.setTempPoint(tempPoint);
        historyInfo.setMaxScore(maxScore);
        historyInfo.setWhiteScore(whiteScore);
        historyInfo.setBlackScore(blackScore);
        historyInfo.setCol(col);
        historyInfo.setRow(row);
        historyInfo.setActionState(actionState);
        historyList.add(GsonUtils.toJson(historyInfo)); //添加历史记录
        turnHistoryList.clear(); //走了新步这反历史就没了
    }

    //还原局内历史记录
    private void restoreInternalHistory(HistoryInfo historyInfo) {
        pieces = historyInfo.getPieces();
        symmetryPieces = historyInfo.getSymmetryPieces();
        fallTip = historyInfo.getFallTip();
        arrowTip = historyInfo.getArrowTip();
        tempPoint = historyInfo.getTempPoint();
        maxScore = historyInfo.getMaxScore();
        whiteScore = historyInfo.getWhiteScore();
        blackScore = historyInfo.getBlackScore();
        col = historyInfo.getCol();
        row = historyInfo.getRow();
        actionState = historyInfo.getActionState();
        refreshUI();
        invalidate();
    }

    //计算绘制参数
    private void calculateParameter() {
        float width = getWidth();
        float height = getHeight();
        float minS = Math.min(width, height);
        int maxL = Math.max(col, row);
        this.placeSize = minS / (maxL + 1); //加1表示最小预留一个格子的边距
        this.firstX = (width - (col - 1) * placeSize) / 2;
        this.firstY = (height - (row - 1) * placeSize) / 2;
    }

    //初始化画笔
    private void initPaint() {
        this.mPaint = new Paint();
        this.mPaint.setAntiAlias(true);
        this.mPaint.setDither(true);
        this.mPaint.setFilterBitmap(true);
    }

    //初始绘制二级缓冲背景
    private void drawBgBuffer() {
        this.bgBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas buffCanvas = new Canvas(bgBuffer);
        //画带光晕的背景色
        float gX = firstX + placeSize * col / 2;
        float gY = firstY + placeSize * row / 2;
        float gR = placeSize * col / 2;
        mPaint.setShader(new RadialGradient(gX, gY, gR, 0xFFEEBE89, 0xFFEBA555, Shader.TileMode.CLAMP));
        buffCanvas.drawPaint(mPaint);
        mPaint.setShader(null);
        mPaint.setStrokeWidth(placeSize / 24f);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        int textSize = (int) (placeSize / 2f);
        mPaint.setTextSize(textSize);
        mPaint.setFakeBoldText(true);
        mPaint.setTextAlign(Paint.Align.CENTER);// 文字原点变为了中下点
        //画线和坐标
        for (int i = 0; i < col; i++) {
            float sX = firstX + i * placeSize;
            float sY = firstY;
            float eX = sX;
            float eY = firstY + (row - 1) * placeSize;
            mPaint.setColor(Color.GRAY);
            buffCanvas.drawLine(sX, sY, eX, eY, mPaint);
            float tX = sX;
            float ty = (sY + textSize) / 2 - sY / 8;
            mPaint.setColor(Color.BLACK);
            buffCanvas.drawText(String.valueOf((char) ('A' + i)), tX, ty, mPaint);
        }
        for (int i = 0; i < row; i++) {
            float sX = firstX;
            float sY = firstY + i * placeSize;
            float eX = firstX + (col - 1) * placeSize;
            float eY = sY;
            mPaint.setColor(Color.GRAY);
            buffCanvas.drawLine(sX, sY, eX, eY, mPaint);
            float tX = sX * 0.4f;
            float ty = sY + textSize / 2f;
            mPaint.setColor(Color.BLACK);
            buffCanvas.drawText(String.valueOf(row - i), tX, ty, mPaint);
        }
        //保证尽量均分的画点
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(placeSize / 8f);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        int centerCol = col / 2;
        int centerRow = row / 2;
        if ((col & 1) == 1 && (row & 1) == 1) {
            buffCanvas.drawPoint(firstX + centerCol * placeSize, firstY + centerRow * placeSize, mPaint);
        }
        int p1C = centerCol / 2;
        int p1R = centerRow / 2;
        buffCanvas.drawPoint(firstX + p1C * placeSize, firstY + p1R * placeSize, mPaint);
        int p2C = col - 1 - p1C;
        int p2R = p1R;
        buffCanvas.drawPoint(firstX + p2C * placeSize, firstY + p2R * placeSize, mPaint);
        int p3C = p1C;
        int p3R = row - 1 - p1R;
        buffCanvas.drawPoint(firstX + p3C * placeSize, firstY + p3R * placeSize, mPaint);
        int p4C = p2C;
        int p4R = p3R;
        buffCanvas.drawPoint(firstX + p4C * placeSize, firstY + p4R * placeSize, mPaint);
    }

    //刷新游戏UI
    private void refreshUI() {
        iGameUI.refreshUI(actionState, blackScore, whiteScore);
    }

    //处理触摸事件
    private void handleEvent() {
        if (getDistance(dX, dY, uX, uY) > placeSize) {
            return; //移动距离超过一格视为放弃落子
        }
        if ((uT - dT) < 50) {
            return; //触摸时间过短视为误触
        }
        if (checkEventRange()) {
            int[] coordinate = getCoordinateByPoint();
            int pCol = coordinate[0];
            int pRow = coordinate[1];
            processFallAction(pCol, pRow);
        }
    }

    //处理落子行为
    private void processFallAction(int pCol, int pRow) {
        //根据坐标获取该位置的信息，为null就是空位置，不为空就是已有棋子了
        ChessInfo chessInfo = getChessByPoint(pCol, pRow);
        arrowTip = null; //默认清除箭头提示
        //等待黑棋落子
        if (checkState(ActionState.WAIT_FALL_BLACK)) {
            //落在空白处
            if (chessInfo == null) {
                fallPiece(true, pCol, pRow); //落下黑棋子
                setFallTip(pCol, pRow); //添加落子提示
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
                //黑棋成型了
                if (checkSymmetryShape(true)) {
                    changeState(ActionState.REMOVE_SHAPED_BLACK);//切换到黑棋快捷提子状态
                }
                //黑棋没成型
                else {
                    changeState(ActionState.WAIT_FALL_WHITE);//自动交换白棋行棋
                }
            }
            //落在有子处
            else {
                setFallTip(pCol, pRow); //添加落子提示
                setTempPoint(pCol, pRow); //记录落子位置
                //落子已有的黑棋处，可以移动黑棋
                if (chessInfo.isBlack) {
                    iGameUI.playSound(SoundData.Type.PICK); //播放声音
                    pieces.remove(chessInfo); //移除该棋子
                    changeState(ActionState.THEN_MOVE_BLACK); //切换到移动黑子的状态
                }
                //落在已有的白棋处
                else {
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    iGameUI.playSound(SoundData.Type.PICK); //播放声音
                    chessInfo.isBlack = true; //白棋变为黑棋
                    changeState(ActionState.PUSH_AWAY_WHITE); //切换到挤走白子的状态
                }
            }
            addHistory();
        }
        //等待白棋落子
        else if (checkState(ActionState.WAIT_FALL_WHITE)) {
            //落在空白处
            if (chessInfo == null) {
                fallPiece(false, pCol, pRow); //落下白棋子
                setFallTip(pCol, pRow); //添加落子提示
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
                //白棋成型了
                if (checkSymmetryShape(false)) {
                    changeState(ActionState.REMOVE_SHAPED_WHITE);//切换到白棋快捷提子状态
                }
                //白棋没成型
                else {
                    changeState(ActionState.WAIT_FALL_BLACK);//自动交换黑棋行棋
                }
            }
            else {
                setFallTip(pCol, pRow); //添加落子提示
                setTempPoint(pCol, pRow); //记录落子位置
                //落子已有的白棋处，可以移动白棋
                if (!chessInfo.isBlack) {
                    iGameUI.playSound(SoundData.Type.PICK); //播放声音
                    pieces.remove(chessInfo); //移除该棋子
                    changeState(ActionState.THEN_MOVE_WHITE); //切换到移动白子的状态
                }
                //落在已有的黑棋处
                else {
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    iGameUI.playSound(SoundData.Type.PICK); //播放声音
                    chessInfo.isBlack = false; //黑棋变为白棋
                    changeState(ActionState.PUSH_AWAY_BLACK); //切换到挤走黑子的状态
                }
            }
            addHistory();//添加到历史记录
        }
        //等待黑方移动黑棋
        else if (checkState(ActionState.THEN_MOVE_BLACK)) {
            //移动到空白位置
            if (chessInfo == null) {
                //移动到不同位置
                if (tempPoint.x != pCol || tempPoint.y != pRow) {
                    fallPiece(true, pCol, pRow); //移动黑棋到此处
                    setFallTip(tempPoint.x, tempPoint.y); //添加上一步落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    //黑棋成型了
                    if (checkSymmetryShape(true)) {
                        changeState(ActionState.REMOVE_SHAPED_BLACK);//切换到黑棋快捷提子状态
                    }
                    //黑棋没成型
                    else {
                        changeState(ActionState.WAIT_FALL_WHITE);//自动交换白棋行棋
                    }
                    addHistory(); //添加到历史记录
                }
                //移动到原位置
                else {
                    iGameUI.toast("不能移动到这个位置"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                    setFallTip(tempPoint.x, tempPoint.y); //还原聚焦提示
                }
            }
            //移动的位置有棋
            else {
                iGameUI.toast("不能移动到这个位置"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                setFallTip(tempPoint.x, tempPoint.y); //还原落子提示
            }
        }
        //等待白方移动白棋
        else if (checkState(ActionState.THEN_MOVE_WHITE)) {
            //移动到空白位置
            if (chessInfo == null) {
                //移动到不同位置
                if (tempPoint.x != pCol || tempPoint.y != pRow) {
                    fallPiece(false, pCol, pRow); //移动白棋到此处
                    setFallTip(tempPoint.x, tempPoint.y); //添加上一步落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    //白棋成型了
                    if (checkSymmetryShape(false)) {
                        changeState(ActionState.REMOVE_SHAPED_WHITE);//切换到白棋快捷提子状态
                    }
                    //白棋没成型
                    else {
                        changeState(ActionState.WAIT_FALL_BLACK);//自动交换黑棋行棋
                    }
                    addHistory(); //添加到历史记录
                }
                //移动到原位置
                else {
                    iGameUI.toast("不能移动到这个位置"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                    setFallTip(tempPoint.x, tempPoint.y); //还原聚焦提示
                }
            }
            //移动的位置有棋
            else {
                iGameUI.toast("不能移动到这个位置"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                setFallTip(tempPoint.x, tempPoint.y); //还原落子提示
            }
        }
        //等待黑方挤走白棋的位置
        else if (checkState(ActionState.PUSH_AWAY_WHITE)) {
            //挤走位置相邻
            if (Math.abs(tempPoint.x - pCol) < 2 && Math.abs(tempPoint.y - pRow) < 2) {
                //放到空白位置
                if (chessInfo == null) {
                    fallPiece(false, pCol, pRow); //把白棋挤到这个位置
                    setFallTip(tempPoint.x, tempPoint.y); //添加上一步的落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    //判断黑棋成型了
                    if (checkSymmetryShape(true)) {
                        changeState(ActionState.REMOVE_SHAPED_BLACK);//切换到黑棋快捷提子状态
                    }
                    //黑棋没成型
                    else {
                        changeState(ActionState.WAIT_FALL_WHITE);//自动交换白棋行棋
                    }
                    addHistory(); //添加到历史记录
                }
                //移动的位置有棋
                else {
                    iGameUI.toast("只能挤到无棋的位置"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                    setFallTip(tempPoint.x, tempPoint.y); //还原落子提示
                }
            }
            //挤走的位置不相邻
            else {
                iGameUI.toast("只能挤到相邻一圈的位置"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                setFallTip(tempPoint.x, tempPoint.y); //还原落子提示
            }
        }
        //等待白方挤走黑棋的位置
        else if (checkState(ActionState.PUSH_AWAY_BLACK)) {
            //挤走位置相邻
            if (Math.abs(tempPoint.x - pCol) < 2 && Math.abs(tempPoint.y - pRow) < 2) {
                //放到空白位置
                if (chessInfo == null) {
                    fallPiece(true, pCol, pRow); //把黑棋挤到这个位置
                    setFallTip(tempPoint.x, tempPoint.y); //添加上一步的落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    //判断白棋成型了
                    if (checkSymmetryShape(false)) {
                        changeState(ActionState.REMOVE_SHAPED_WHITE);//切换到白棋快捷提子状态
                    }
                    //黑棋没成型
                    else {
                        changeState(ActionState.WAIT_FALL_BLACK);//自动交换黑棋行棋
                    }
                    addHistory(); //添加到历史记录
                }
                //移动的位置有棋
                else {
                    iGameUI.toast("只能挤到无棋的位置"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
            }
            //挤走的位置不相邻
            else {
                iGameUI.toast("只能挤到相邻一圈的位置"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待提走成型的黑棋
        else if (checkState(ActionState.REMOVE_SHAPED_BLACK)) {
            if (symmetryPieces.contains(chessInfo)) {
                int size = symmetryPieces.size();
                iGameUI.playSound(SoundData.Type.PICK, size - 1); //一连串提子的声音
                pieces.removeAll(symmetryPieces); //批量提子
                int score = size - 5; //计算得分
                blackScore += score; //加分
                if (blackScore >= maxScore) {
                    changeState(ActionState.BLACK_WIN); //黑棋获胜
                }
                else {
                    changeState(ActionState.WAIT_FALL_BLACK); //提子后继续走黑棋
                }
                addHistory();
            }
            else {
                iGameUI.toast("请点击成型的区域快捷提子"); //提示用户正确的操作
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待提走成型的白棋
        else if (checkState(ActionState.REMOVE_SHAPED_WHITE)) {
            if (symmetryPieces.contains(chessInfo)) {
                int size = symmetryPieces.size();
                iGameUI.playSound(SoundData.Type.PICK, size - 1); //一连串提子的声音
                pieces.removeAll(symmetryPieces); //批量提子
                int score = size - 5; //计算得分
                whiteScore += score; //加分
                if (whiteScore >= maxScore) {
                    changeState(ActionState.WHITE_WIN); //白棋获胜
                }
                else {
                    changeState(ActionState.WAIT_FALL_WHITE); //提子后继续走白棋
                }
                addHistory();
            }
            else {
                iGameUI.toast("请点击成型的区域快捷提子"); //提示用户正确的操作
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //强制持续下黑棋
        else if (checkState(ActionState.ENFORCE_BLACK)) {
            //落子空白处
            if (chessInfo == null) {
                fallPiece(true, pCol, pRow); //落下黑棋
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
            }
            //落子白棋处
            else if (!chessInfo.isBlack) {
                chessInfo.isBlack = true; //原来的白棋强制变为黑棋
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
                iGameUI.playSound(SoundData.Type.PICK); //播放声音
            }
            //落在黑棋处
            else {
                pieces.remove(chessInfo); //移除该黑棋
                iGameUI.playSound(SoundData.Type.PICK); //播放声音
            }
            symmetryPieces.clear(); //清空成型棋子列表
            setFallTip(pCol, pRow); //添加落子提示
            addHistory();//添加到历史记录
        }
        //强制持续下白棋
        else if (checkState(ActionState.ENFORCE_WHITE)) {
            //落在空白处
            if (chessInfo == null) {
                fallPiece(false, pCol, pRow); //落下白棋
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
            }
            //落在白棋处
            else if (chessInfo.isBlack) {
                chessInfo.isBlack = false; //强制黑棋变白棋
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
                iGameUI.playSound(SoundData.Type.PICK); //播放声音
            }
            //落在白棋处
            else {
                pieces.remove(chessInfo); //移除该白棋
                iGameUI.playSound(SoundData.Type.PICK); //播放声音
            }
            symmetryPieces.clear(); //清空成型棋子列表
            setFallTip(pCol, pRow); //添加落子提示
            addHistory();//添加到历史记录
        }
        //等待移除棋子
        else if (checkState(ActionState.CONTINUE_DELETE)) {
            if (chessInfo != null) {
                symmetryPieces.clear(); //清空成型棋子列表
                pieces.remove(chessInfo); //移除该棋子
                setFallTip(pCol, pRow); //添加落子提示
                iGameUI.playSound(SoundData.Type.PICK); //播放声音
                addHistory(); //添加到历史记录
            }
            else {
                setFallTip(pCol, pRow); //添加落子提示
            }
        }
        invalidate(); //每次触发事件不管有没有变化都通知棋盘重绘
    }

    //记录落子位置
    private void setTempPoint(int pCol, int pRow) {
        tempPoint = new Point(pCol, pRow);
    }

    //判断当前状态
    private boolean checkState(ActionState state) {
        return actionState == state;
    }

    //转换状态
    private void changeState(ActionState state) {
        actionState = state;
        refreshUI();
    }

    //在棋盘上指定坐标上落下新棋子
    private void fallPiece(boolean isBlack, int pCol, int pRow) {
        float enterX = firstX + pCol * placeSize; //坐标对应的交叉点的x坐标
        float enterY = firstY + pRow * placeSize;//坐标对应的交叉点的y坐标
        float halfSize = placeSize / 2; //格子距离的一半，用于计算
        ChessInfo p = new ChessInfo(isBlack, pCol, pRow, enterX - halfSize, enterY - halfSize); //创建新的黑棋
        pieces.add(p); //落下黑子
    }

    //在指定坐标上添加落子提示
    private void setFallTip(int pCol, int pRow) {
        float enterX = firstX + pCol * placeSize; //坐标对应的交叉点的x坐标
        float enterY = firstY + pRow * placeSize;//坐标对应的交叉点的y坐标
        float halfSize = placeSize / 2; //格子距离的一半，用于计算
        float offsetSize = halfSize * 0.85f;
        float radiusSize = halfSize;
        fallTip = new FallTipInfo(pCol, pRow, enterX - offsetSize, enterY - offsetSize, enterX + offsetSize, enterY + offsetSize, enterX, enterY, radiusSize);//增加落子提示图标
    }

    //指定坐标设定箭头提示
    private void setArrowTip(int pCol, int pRow) {
        float startX = firstX + tempPoint.x * placeSize;
        float startY = firstY + tempPoint.y * placeSize;
        float endX = firstX + pCol * placeSize;
        float endY = firstY + pRow * placeSize;
        arrowTip = new ArrowTipInfo(pCol, pRow, startX, startY, endX, endY);
    }

    //推测当前应该是黑棋行动还是白棋行动
    private boolean guessActionSide() {
        int blackCount = 0;
        int whiteCount = 0;
        for (ChessInfo piece : pieces) {
            if (piece.isBlack) {
                blackCount++;
            }
            else {
                whiteCount++;
            }
        }
        return (blackCount - whiteCount) % 2 == 0;
    }

    //检查是否形成了对称图形
    private boolean checkSymmetryShape(boolean isBlackChess) {
        boolean isSymmetryShaped = false;
        symmetryPieces.clear();
        List<ChessInfo> iChess = new ArrayList<>(pieces);
        iChess.removeIf(chessInfo -> chessInfo.isBlack != isBlackChess); //保留同色棋子集合
        Map<ChessInfo, Set<ChessInfo>> adjacentGroup = new HashMap<>(); //相邻棋子组成的组
        for (ChessInfo key : iChess) {
            Set<ChessInfo> set = new HashSet<>();
            set.add(key);
            adjacentGroup.put(key, set);
        }
        int size = iChess.size();
        for (int i = 0; i < size; i++) { //第一次遍历所有相邻棋子建立相邻关系
            for (int j = i + 1; j < size; j++) { //保证任意两个棋子只判断一次
                ChessInfo a = iChess.get(i);
                ChessInfo b = iChess.get(j);
                if (isAdjacent(a, b)) {
                    adjacentGroup.get(a).add(b);
                    adjacentGroup.get(b).add(a);
                }
            }
        }
        //printAdjacentGroup("第一次建立关系", adjacentGroup);
        Map<ChessInfo, Set<ChessInfo>> blockGroup = new HashMap<>();
        for (int i = 0; i < size; i++) { //第二次遍历所有邻组合并
            ChessInfo a = iChess.get(i);
            Set<ChessInfo> aSet = adjacentGroup.get(a);
            for (ChessInfo blockKey : blockGroup.keySet()) {  //判断这一组是否和已经有的块有交集，保证块不重复
                Set<ChessInfo> blockSet = blockGroup.get(blockKey);
                if (isIntersection(aSet, blockSet)) { //如果有的话
                    a = blockKey;   //就还是用最早的键来判断
                    blockSet.addAll(aSet); //且把这一组的所有棋子添加到块中
                    aSet = blockSet; //且用最全的集合来判断
                }
            }
            for (int j = i + 1; j < size; j++) {
                ChessInfo b = iChess.get(j);
                Set<ChessInfo> bSet = adjacentGroup.get(b);
                if (isIntersection(aSet, bSet)) {
                    aSet.addAll(bSet); //判断过有交集的都要收集起来以免后面的漏判
                    blockGroup.put(a, aSet); //这里的a始终是最早的块键
                }
            }
        }
        //printAdjacentGroup("合并邻组后", blockGroup);
        for (ChessInfo key : blockGroup.keySet()) {
            Set<ChessInfo> blockSet = blockGroup.get(key);
            if (blockSet.size() >= 6) { //棋子数量小于6的块都不去考虑了
                int minCol = -1, minRow = -1, maxCol = -1, maxRow = -1;
                for (ChessInfo chessInfo : blockSet) { //获取区间范围
                    if (minCol == -1) { //注意第一次赋予初值
                        minCol = chessInfo.col;
                        maxCol = chessInfo.col;
                        minRow = chessInfo.row;
                        maxRow = chessInfo.row;
                    }
                    if (chessInfo.col > maxCol) {
                        maxCol = chessInfo.col;
                    }
                    if (chessInfo.col < minCol) {
                        minCol = chessInfo.col;
                    }
                    if (chessInfo.row > maxRow) {
                        maxRow = chessInfo.row;
                    }
                    if (chessInfo.row < minRow) {
                        minRow = chessInfo.row;
                    }
                }
                float midCol = (minCol + maxCol) / 2f; //获取中心点横坐标，用于后续计算
                float midRow = (minRow + maxRow) / 2f; //获取中心点纵坐标，用于后续计算
                if (checkHorizontalSymmetry(blockSet, midCol)) {
                    isSymmetryShaped = true;
                    symmetryPieces.addAll(blockSet);
                }
                else if (checkVerticalSymmetry(blockSet, midRow)) {
                    isSymmetryShaped = true;
                    symmetryPieces.addAll(blockSet);
                }
                else if (checkPositiveSymmetry(blockSet, midCol, midRow)) {
                    isSymmetryShaped = true;
                    symmetryPieces.addAll(blockSet);
                }
                else if (checkNegativeSymmetry(blockSet, midCol, midRow)) {
                    isSymmetryShaped = true;
                    symmetryPieces.addAll(blockSet);
                }
            }
        }
        return isSymmetryShaped;
    }

    //判断是否水平对称无斜率（一条竖线两边全等）
    private boolean checkHorizontalSymmetry(Set<ChessInfo> blockChessSet, float midCol) {
        Set<Point> blockSet = new HashSet<>();
        for (ChessInfo chessInfo : blockChessSet) {
            blockSet.add(new Point(chessInfo.col, chessInfo.row));
        }
        Set<Point> left = new HashSet<>();
        Set<Point> right = new HashSet<>();
        for (Point chessInfo : blockSet) {
            if (chessInfo.x < midCol) {
                left.add(chessInfo);
                float diff = midCol - chessInfo.x; //计算要翻转的值
                chessInfo.x = (int) (midCol + diff); //翻转坐标
            }
            else if (chessInfo.x > midCol) {
                right.add(chessInfo);
            }
        }
        boolean isFlag = isSameCoordinate(left, right); //判断两组棋子横纵坐标是否全等
        return isFlag;
    }

    //判断是否垂直对称（一条横线两边全等）
    private boolean checkVerticalSymmetry(Set<ChessInfo> blockChessSet, float midRow) {
        Set<Point> blockSet = new HashSet<>();
        for (ChessInfo chessInfo : blockChessSet) {
            blockSet.add(new Point(chessInfo.col, chessInfo.row));
        }
        Set<Point> top = new HashSet<>();
        Set<Point> bottom = new HashSet<>();
        for (Point chessInfo : blockSet) {
            if (chessInfo.y < midRow) {
                top.add(chessInfo);
                float diff = midRow - chessInfo.y; //计算要翻转的值
                chessInfo.y = (int) (midRow + diff); //翻转坐标
            }
            else if (chessInfo.y > midRow) {
                bottom.add(chessInfo);
            }
        }
        boolean isFlag = isSameCoordinate(top, bottom);
        return isFlag;
    }

    //判断是否以45°的斜线对称
    private boolean checkPositiveSymmetry(Set<ChessInfo> blockChessSet, float midCol, float midRow) {
        Set<Point> blockSet = new HashSet<>();
        for (ChessInfo chessInfo : blockChessSet) {
            blockSet.add(new Point(chessInfo.col, chessInfo.row));
        }
        Set<Point> leftTop = new HashSet<>();
        Set<Point> rightBottom = new HashSet<>();
        float split = midCol + midRow;
        for (Point chessInfo : blockSet) {
            if (chessInfo.y + chessInfo.x < split) {
                leftTop.add(chessInfo);
                //先求两条直线的交点
                float b = midCol + midRow; //根据公式y = ax + b;a=-1; 求对称线的公式需要的参数
                float h = chessInfo.y - chessInfo.x; //求对齐线的公式需要的参数
                float jX = (b - h) / 2; //求出垂线交点x
                float jY = (b + h) / 2;//求出垂线交点y
                float diffX = jX - chessInfo.x;
                chessInfo.x = (int) (jX + diffX);
                float diffY = jY - chessInfo.y;
                chessInfo.y = (int) (jY + diffY);
            }
            else if (chessInfo.y + chessInfo.x > split) {
                rightBottom.add(chessInfo);
            }
        }
        boolean isFlag = isSameCoordinate(leftTop, rightBottom);
        return isFlag;
    }

    //判断是否以-45°的斜线对称
    private boolean checkNegativeSymmetry(Set<ChessInfo> blockChessSet, float midCol, float midRow) {
        Set<Point> blockSet = new HashSet<>();
        for (ChessInfo chessInfo : blockChessSet) {
            blockSet.add(new Point(chessInfo.col, chessInfo.row));
        }
        Set<Point> left = new HashSet<>();
        Set<Point> right = new HashSet<>();
        for (Point chessInfo : blockSet) {  //先全部水平翻转，这样midCol和midRow不会变，而变成可以以正45°斜线对称了
            if (chessInfo.x < midCol) {
                left.add(chessInfo);
                float diff = midCol - chessInfo.x;
                chessInfo.x = (int) (midCol + diff);
            }
            else if (chessInfo.x > midCol) {
                right.add(chessInfo);
                float diff = chessInfo.x - midCol;
                chessInfo.x = (int) (midCol - diff); //还原改变
            }
        }
        Set<Point> leftTop = new HashSet<>();
        Set<Point> rightBottom = new HashSet<>();
        float split = midCol + midRow;
        for (Point chessInfo : blockSet) {
            if (chessInfo.y + chessInfo.x < split) {
                leftTop.add(chessInfo);
                float b = midCol + midRow; //根据公式y = ax + b;a=-1; 求对称线的公式需要的参数
                float h = chessInfo.y - chessInfo.x; //求对齐线的公式需要的参数
                float jX = (b - h) / 2; //求出垂线交点x
                float jY = (b + h) / 2;//求出垂线交点y
                float diffX = jX - chessInfo.x;
                chessInfo.x = (int) (jX + diffX);
                float diffY = jY - chessInfo.y;
                chessInfo.y = (int) (jY + diffY);
            }
            else if (chessInfo.y + chessInfo.x > split) {
                rightBottom.add(chessInfo);
            }
        }
        boolean isFlag = isSameCoordinate(leftTop, rightBottom);
        return isFlag;
    }

    //打印邻组，邻组是我算法里的一个中间概念
    private void printAdjacentGroup(String title, Map<?, Set<ChessInfo>> adjacentGroup) {
        System.out.println(title + "有" + adjacentGroup.size() + "组");
        for (Object key : adjacentGroup.keySet()) {
            System.out.print(key.toString() + "组有:");
            for (ChessInfo chessInfo : adjacentGroup.get(key)) {
                System.out.print(chessInfo.toString());
            }
            System.out.println("。");
        }
    }

    //判断两个棋子是否相邻(直向和斜向相邻都算,位置相同不算)
    private boolean isAdjacent(ChessInfo a, ChessInfo b) {
        if (a.col == b.col && a.row == b.row) {
            return false;
        }
        else {
            return Math.abs(a.col - b.col) <= 1 && Math.abs(a.row - b.row) <= 1;
        }
    }

    //判断两组棋子横纵坐标是否全等
    private boolean isSameCoordinate(Set<Point> aSet, Set<Point> bSet) {
        if (aSet.size() != bSet.size()) {
            return false;
        }
        else {
            for (Point chessInfo : aSet) {
                boolean same = false;
                for (Point info : bSet) {
                    if (info.y == chessInfo.y && info.x == chessInfo.x) {
                        same = true;
                        break;
                    }
                }
                if (!same) {
                    return false;
                }
            }
        }
        return true;
    }

    //判断两个邻组是否有交集（只要有一个棋子双方都存在就算有交集）
    private boolean isIntersection(Set<ChessInfo> aSet, Set<ChessInfo> bSet) {
        Set<ChessInfo> resSet = new HashSet<>(aSet);
        resSet.retainAll(bSet);
        return resSet.size() > 0;
    }

    //根据坐标点获取棋子
    private ChessInfo getChessByPoint(int pCol, int pRow) {
        for (ChessInfo piece : pieces) {
            if (piece.col == pCol && piece.row == pRow) {
                return piece;
            }
        }
        return null;
    }

    //判断点击事件是否在有效范围内
    private boolean checkEventRange() {
        float halfSize = placeSize / 2;
        float minX = firstX - halfSize;
        float maxX = firstX + col * placeSize - halfSize;
        float minY = firstY - halfSize;
        float maxY = firstY + row * placeSize - halfSize;
        return uX > minX && uX < maxX && uY > minY && uY < maxY;
    }

    //根据点击事件获取坐标
    private int[] getCoordinateByPoint() {
        int[] c = new int[2];
        int x = (int) ((uX - firstX + placeSize / 2) / placeSize);
        int y = (int) ((uY - firstY + placeSize / 2) / placeSize);
        c[0] = x;
        c[1] = y;
        return c;
    }

    //获取两点之间的距离
    private float getDistance(float x0, float y0, float x1, float y1) {
        float a = (y0 - y1) * (y0 - y1) + (x0 - x1) * (x0 - x1);
        return (float) Math.sqrt(a);
    }

    //获取指定大小的图片
    private Bitmap getBitmap(@DrawableRes int sourceId, float width, float height) {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), sourceId);
        if (bitmap.getWidth() == width && bitmap.getHeight() == height) {    //写这个判断，避createScaledBitmap()的坑
            return bitmap;
        }
        return Bitmap.createScaledBitmap(bitmap, (int) width, (int) height, true);
    }

}
